/*
    asyframe for asyframe@gmail.com

    Copyright (C) 2011, asyframe@gmail.com, http://asyframe.googlecode.com/

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "stdafx.h"
#include "ConstDef.h"
#include "Common/Logger.h"
#include "Common/Interface.h"
#include "Common/ThreadHandler.h"
#include "Common/WorkerBase.h"
#include "Common/AppBase.h"
#include "Common/BufferPool.h"
#include "Common/ActorBase.h"
#include "Common/IoBuffer.h"
#include "Network/DataHandler.h"
#include "Network/SSLHandler.h"

namespace asyframe
{
	ISSLTcpAction::ISSLTcpAction()
	{

	}

	ISSLTcpAction::~ISSLTcpAction()
	{

	}

	retT ISSLTcpAction::onHandshake( boost::shared_ptr<DataHandlerContext> _pDataHandlerContext, const boost::system::error_code& e, std::size_t _timeoutCount )
	{
		R(handleTimeout(_pDataHandlerContext, e, _timeoutCount));
		RETSUCCESS;
	}

////////////////////////////////////////////////////////////////////////////////////////////

	SSLContextBuilder::SSLContextBuilder()
	{

	}

	SSLContextBuilder::~SSLContextBuilder()
	{

	}

	retT SSLContextBuilder::makeSSLContext( boost::shared_ptr<boost::asio::io_service> _pIoService, boost::shared_ptr<boost::asio::ssl::context>& _pSSLContext )
	{
		if(!_pSSLContext)
		{
			_pSSLContext.reset(new boost::asio::ssl::context(*_pIoService, boost::asio::ssl::context::sslv23));
		}
		RETSUCCESS;
	}

	SSLTcpHandler::SSLTcpHandler(boost::shared_ptr<SSLContextBuilder> _pSSLContextBuilder)
		:TcpHandler(),pSSLContextBuilder_(_pSSLContextBuilder),pSSLContext_(),pSSLSocket_()
	{

	}

	SSLTcpHandler::~SSLTcpHandler()
	{
		pSSLContextBuilder_.reset();
		pSSLContext_.reset();
		pSSLSocket_.reset();
	}

	retT SSLTcpHandler::bind( boost::shared_ptr<WorkerBase> _pWorker )
	{
		R(TcpHandler::bind(_pWorker));
		if(pSSLContextBuilder_)
		{
			R(pSSLContextBuilder_->makeSSLContext(_pWorker->getIoService(), pSSLContext_));
		}
		else
			RET(ERROR_COMMON_SHARED_PTR_NULL);
		pSSLSocket_.reset(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket&>(*pSocket_, *pSSLContext_));
		RETSUCCESS;
	}

	retT SSLTcpHandler::close()
	{
		R(DataHandler::close());
		if(pSSLSocket_)
		{
			boost::system::error_code ignoredEc;
			pSSLSocket_->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignoredEc);
			pSSLSocket_->lowest_layer().close();
		}
		RETSUCCESS;
	}

	retT SSLTcpHandler::asyncHandshake( boost::asio::ssl::stream_base::handshake_type _type, boost::shared_ptr<DataHandlerContext> _pDataHandlerContext, BaseCallBackFunType _dataCallBackFun )
	{
		if(_dataCallBackFun == NULL)
		{
			if(boost::shared_ptr<ISSLTcpAction> pISSLTcpAction = boost::dynamic_pointer_cast<ISSLTcpAction>(pHandlerOwner_.lock()))
			{
				_dataCallBackFun = boost::bind(&ISSLTcpAction::onHandshake, pISSLTcpAction, _pDataHandlerContext, _1, _2);
			}
			else
				RET(ERROR_HANDLERBASE_HANDLEROWNER_NOT_EXIST);
		}
		R(operationStart(_pDataHandlerContext, _dataCallBackFun));
		if(!pSSLSocket_)
			RET(ERROR_COMMON_SHARED_PTR_NULL);
		pSSLSocket_->async_handshake(_type,
			boost::bind(_dataCallBackFun,
			boost::asio::placeholders::error,
			0));
		RETSUCCESS;
	}

	retT SSLTcpHandler::asyncRead( boost::shared_ptr<IoBuffer> _pBuffers, boost::shared_ptr<DataHandlerContext> _pDataHandlerContext, DataCallBackFunType _dataCallBackFun )
	{
		if(_dataCallBackFun == NULL)
		{
			if(boost::shared_ptr<ISSLTcpAction> pISSLTcpAction = boost::dynamic_pointer_cast<ISSLTcpAction>(pHandlerOwner_.lock()))
			{
				_dataCallBackFun = boost::bind(&ISSLTcpAction::onRead, pISSLTcpAction, _pDataHandlerContext, _1, _2, _3);
			}
			else
				RET(ERROR_HANDLERBASE_HANDLEROWNER_NOT_EXIST);
		}
		R(operationStart(_pDataHandlerContext, boost::bind(_dataCallBackFun, 0, _1, _2)));
		if(!_pBuffers || !pSSLSocket_)
			RET(ERROR_COMMON_SHARED_PTR_NULL);
		if(_pDataHandlerContext)
		{
			if(!_pDataHandlerContext->pBuffer_)
				_pDataHandlerContext->pBuffer_ = _pBuffers;
		}
		std::size_t bytesToTransfer;
		if(_pDataHandlerContext && _pDataHandlerContext->bytesToTransfer_)
		{
			bytesToTransfer = _pDataHandlerContext->bytesToTransfer_;
			if(bytesToTransfer > _pBuffers->space())
			{
				LOG_WARN(boost::format( "IoBuffer outof space in %1% %2% %3%" ) % __FUNCTION__ % __FILE__ % __LINE__);
				RET(INFO_DATAHANDLER_IOBUFFER_OUTOF_SPACE);
			}
		}
		else
			bytesToTransfer = _pBuffers->space();
		boost::asio::async_read(*pSSLSocket_,
			boost::asio::buffer(_pBuffers->blank(),bytesToTransfer),
			boost::bind(_dataCallBackFun,
			boost::asio::placeholders::bytes_transferred,
			boost::asio::placeholders::error,
			0));
		RETSUCCESS;
	}

	retT SSLTcpHandler::asyncWrite( boost::shared_ptr<IoBuffer> _pBuffers, boost::shared_ptr<DataHandlerContext> _pDataHandlerContext, DataCallBackFunType _dataCallBackFun )
	{
		if(_dataCallBackFun == NULL)
		{
			if(boost::shared_ptr<ISSLTcpAction> pISSLTcpAction = boost::dynamic_pointer_cast<ISSLTcpAction>(pHandlerOwner_.lock()))
			{
				_dataCallBackFun = boost::bind(&ISSLTcpAction::onWrite, pISSLTcpAction, _pDataHandlerContext, _1, _2, _3);
			}
			else
				RET(ERROR_HANDLERBASE_HANDLEROWNER_NOT_EXIST);
		}
		R(operationStart(_pDataHandlerContext, boost::bind(_dataCallBackFun, 0, _1, _2)));
		if(!_pBuffers || !pSSLSocket_)
			RET(ERROR_COMMON_SHARED_PTR_NULL);
		if(_pDataHandlerContext)
		{
			if(!_pDataHandlerContext->pBuffer_)
				_pDataHandlerContext->pBuffer_ = _pBuffers;
		}
		std::size_t bytesToTransfer;
		if(_pDataHandlerContext && _pDataHandlerContext->bytesToTransfer_)
		{
			bytesToTransfer = _pDataHandlerContext->bytesToTransfer_;
			if(bytesToTransfer > _pBuffers->size())
			{
				LOG_WARN(boost::format( "IoBuffer outof space in %1% %2% %3%" ) % __FUNCTION__ % __FILE__ % __LINE__);
				RET(INFO_DATAHANDLER_IOBUFFER_OUTOF_SPACE);
			}
		}
		else
			bytesToTransfer = _pBuffers->size();
		boost::asio::async_write(*pSSLSocket_,
			boost::asio::buffer(_pBuffers->data(), bytesToTransfer),
			boost::bind(_dataCallBackFun,
			boost::asio::placeholders::bytes_transferred,
			boost::asio::placeholders::error,
			0));
		RETSUCCESS;
	}

	retT SSLTcpHandler::asyncReadSome( boost::shared_ptr<IoBuffer> _pBuffers, boost::shared_ptr<DataHandlerContext> _pDataHandlerContext, DataCallBackFunType _dataCallBackFun )
	{
		if(_dataCallBackFun == NULL)
		{
			if(boost::shared_ptr<ISSLTcpAction> pISSLTcpAction = boost::dynamic_pointer_cast<ISSLTcpAction>(pHandlerOwner_.lock()))
			{
				_dataCallBackFun = boost::bind(&ISSLTcpAction::onReadSome, pISSLTcpAction, _pDataHandlerContext, _1, _2, _3);
			}
			else
				RET(ERROR_HANDLERBASE_HANDLEROWNER_NOT_EXIST);
		}
		R(operationStart(_pDataHandlerContext, boost::bind(_dataCallBackFun, 0, _1, _2)));
		if(!_pBuffers || !pSSLSocket_)
			RET(ERROR_COMMON_SHARED_PTR_NULL);
		if(_pDataHandlerContext)
		{
			if(!_pDataHandlerContext->pBuffer_)
				_pDataHandlerContext->pBuffer_ = _pBuffers;
		}
		std::size_t bytesToTransfer;
		if(_pDataHandlerContext && _pDataHandlerContext->bytesToTransfer_)
		{
			bytesToTransfer = _pDataHandlerContext->bytesToTransfer_;
			if(bytesToTransfer > _pBuffers->space())
			{
				LOG_WARN(boost::format( "IoBuffer outof space in %1% %2% %3%" ) % __FUNCTION__ % __FILE__ % __LINE__);
				RET(INFO_DATAHANDLER_IOBUFFER_OUTOF_SPACE);
			}
		}
		else
			bytesToTransfer = _pBuffers->space();
		pSSLSocket_->async_read_some(
			boost::asio::buffer(_pBuffers->blank(), bytesToTransfer),
			boost::bind(_dataCallBackFun,
			boost::asio::placeholders::bytes_transferred,
			boost::asio::placeholders::error,
			0));
		RETSUCCESS;
	}

	retT SSLTcpHandler::asyncWriteSome( boost::shared_ptr<IoBuffer> _pBuffers, boost::shared_ptr<DataHandlerContext> _pDataHandlerContext, DataCallBackFunType _dataCallBackFun )
	{
		if(_dataCallBackFun == NULL)
		{
			if(boost::shared_ptr<ISSLTcpAction> pISSLTcpAction = boost::dynamic_pointer_cast<ISSLTcpAction>(pHandlerOwner_.lock()))
			{
				_dataCallBackFun = boost::bind(&ISSLTcpAction::onWriteSome, pISSLTcpAction, _pDataHandlerContext, _1, _2, _3);
			}
			else
				RET(ERROR_HANDLERBASE_HANDLEROWNER_NOT_EXIST);
		}
		R(operationStart(_pDataHandlerContext, boost::bind(_dataCallBackFun, 0, _1, _2)));
		if(!_pBuffers || !pSSLSocket_)
			RET(ERROR_COMMON_SHARED_PTR_NULL);
		if(_pDataHandlerContext)
		{
			if(!_pDataHandlerContext->pBuffer_)
				_pDataHandlerContext->pBuffer_ = _pBuffers;
		}
		std::size_t bytesToTransfer;
		if(_pDataHandlerContext && _pDataHandlerContext->bytesToTransfer_)
		{
			bytesToTransfer = _pDataHandlerContext->bytesToTransfer_;
			if(bytesToTransfer > _pBuffers->size())
			{
				LOG_WARN(boost::format( "IoBuffer outof space in %1% %2% %3%" ) % __FUNCTION__ % __FILE__ % __LINE__);
				RET(INFO_DATAHANDLER_IOBUFFER_OUTOF_SPACE);
			}
		}
		else
			bytesToTransfer = _pBuffers->size();
		pSSLSocket_->async_write_some(
			boost::asio::buffer(_pBuffers->data(), bytesToTransfer),
			boost::bind(_dataCallBackFun,
			boost::asio::placeholders::bytes_transferred,
			boost::asio::placeholders::error,
			0));
		RETSUCCESS;
	}

	boost::shared_ptr< boost::asio::ssl::stream<boost::asio::ip::tcp::socket&> > SSLTcpHandler::getSSLSocket()
	{
		return pSSLSocket_;
	}

	retT SSLTcpHandler::reset()
	{
		if(pSSLSocket_)
		{
			boost::system::error_code ignoredEc;
			pSSLSocket_->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignoredEc);
			pSSLSocket_->lowest_layer().close();
		}
		R(DataHandler::reset());
		RETSUCCESS;
	}
} /* namespace asyframe */
